/**
 * File : user.js
 * By : Minglie
 * QQ: 934031452
 * Date :2021.09.30
 * version :1.4.0
 */

(function (window) {

    class WebComponent {
        //缓存组件模板
        static _webComponenrCache={}
        static _isClass=true;
        constructor(props) {
            //生成组件唯一name
            this.selfName = `MingRouter.componentMap.${props.className}.${props["key"]}`;
            this.props=props;
        }

        setState(state) {
            this.state = Object.assign(this.state, state);
        }
        render() {
            return `<div>...</div>`;
        }
        async getTemplate(props){
            if(MingRouter.componentMap[props.className].template){
                let templateContent="";
                if(MingRouter.componentMap[props.className].templateContent){
                    templateContent=MingRouter.componentMap[props.className].templateContent;
                }else {
                    templateContent=await  MingRouter.getTemplateByHtmlUrl(MingRouter.componentMap[props.className].template)
                    MingRouter.componentMap[props.className].templateContent=templateContent;
                }
                templateContent= eval("`"+templateContent+"`");
                return templateContent;
            }else {
                return this.render(props);
            }
        }
        //获取函数组件模板
        static async staticGetTemplate(props){
            if(MingRouter.componentMap[props.className].template){
                let templateContent="";
                if(MingRouter.componentMap[props.className].templateContent){
                    templateContent=MingRouter.componentMap[props.className].templateContent;
                }else {
                    templateContent=await  MingRouter.getTemplateByHtmlUrl(MingRouter.componentMap[props.className].template)
                    MingRouter.componentMap[props.className].templateContent=templateContent;
                }
                templateContent= eval("`"+templateContent+"`");
                return templateContent;
            }
        }


        componentWillUnmount(props){

        }

        componentWillReceiveProps (props){

        }

        componentDidMount() {

        }

    }

    class popEvent {
        constructor(option) {
            /*
             * 接收四个参数：
             * 1，对象的this
             * 2，要监听的元素, 不传则为对象this
             * 3，要监听的事件,默认监听点击事件
             * 4，是否冒泡, 默认冒泡
             * */


            this.eventObj = option.obj;
            this.target = option.target || this.eventObj;
            this.eventType = option.eventType || 'click';
            this.popup = option.popup || true;
            this.bindEvent();
        }
        bindEvent() {
            let _this = this;
            _this.target.addEventListener(_this.eventType, function (ev) {
                let target = ev.target;
                let dataset, parent, num, b;
                popup(target);

                function popup(obj) {
                    if (obj === document) {
                        return false;
                    }
                    dataset = obj.dataset;
                    num = Object.keys(dataset).length;
                    parent = obj.parentNode;
                    if (num < 1) {
                        popup(parent);
                        num = 0;
                    } else {
                        for (b in dataset) {
                            console.log(b);
                            if (_this.eventObj.__proto__[b]) {
                                _this.eventObj.__proto__[b].call(_this.eventObj, {
                                    obj: obj,
                                    ev: ev,
                                    target: dataset[b],
                                    data: _this.eventObj
                                });
                            }
                        }
                        _this.popup && popup(parent);
                    }
                }
            })
        }
    }


    class MingRouter {
        //全局html缓存
        static loadHtmlCache={};
        //page级别css缓存
        static loadCssCache={};
        constructor(el,reactive=false){
            //路由dom节点
            this.el=el;
            //路由处理器
            this._routes = {};
            //当前路由
            this._currentUrl = '';
            //模板中是否含有变量
            this.reactive=reactive;
            this.init();
        }

        static async loadHtml(htmlUrl){
            if(MingRouter.loadHtmlCache[htmlUrl]){
                return MingRouter.loadHtmlCache[htmlUrl];
            }
            return new Promise((resolve,reject)=>{
                fetch(htmlUrl).then(d=>d.text()).then(d=>{
                    MingRouter.loadHtmlCache[htmlUrl]=d;
                    resolve(d)
                })
            })
        }


        static async loadCss(cssUrl){
            let cssContent=""
            if(MingRouter.loadCssCache[cssUrl]){
                cssContent= MingRouter.loadCssCache[cssUrl];
            }else {
                cssContent= await new Promise((resolve,reject)=>{
                    fetch(cssUrl).then(d=>d.text()).then(d=>{
                        MingRouter.loadCssCache[cssUrl]=d;
                        resolve(d)
                    })
                })
            }
            document.querySelector("#pageCss").innerHTML=cssContent;
        }

        static  html(htmlUrl){
            let r=  MingRouter.loadHtmlCache[htmlUrl]||"<h1>wait...</h1>";
            return r;
        }

        route(path,template, callback,componentDidMount){
            if(this._routes[path]){
                return
            }
            let regExp=new RegExp(path)
            this._routes[path] ={
                path,
                regExp,
                callback:callback || function(){},
                componentDidMount:componentDidMount || function(){},
                templateContent:null,
                template:template
            };
        }
        async refresh(){
            let router=this.getRouter();
            if(router==null){
                console.warn("no router",this._currentUrl)
                return;
            }
            await router.callback();
            router.componentDidMount();
        }

        async render(path){
            let router= this.getRouter(path)
            if(router.templateContent){
                let html=router.templateContent;
                this.renderHtml(html);
                return
            }
            let templateContent=await  MingRouter.getTemplateByHtmlUrl(router.template)
            router.templateContent=templateContent
            this.renderHtml(templateContent)
        }

        static replaceHash(hash){
            document.location.replace(this.getFilterUrl(hash));
        }

        init(){
            window.addEventListener('load', this.refresh.bind(this), false);
            window.addEventListener('hashchange', this.refresh.bind(this), false);
        }
        mapping(path,template,componentDidMount){
            let that=this;
            this.route(path,template,async function() {
                that.render(path);
            },componentDidMount);
        }

        getRouter(path){
            if(path!=undefined && path!=null && !path.startsWith("/")){
                path="/"+path;
            }
            this._currentUrl =path || location.hash.slice(1) || '/';
            let router=null
            for (let key in this._routes){
                if(this._routes[key].regExp.test(this._currentUrl)){
                    router=this._routes[key];
                    if(key=="/"){
                        continue
                    }else {
                        break;
                    }
                }
            }
            return router;
        }

        static getTemplateByHtmlUrl(htmlUrl){
            if(htmlUrl.startsWith("#")){
                let temp =  document.querySelector(htmlUrl).innerHTML;
                return temp;
            }
            if(htmlUrl.endsWith(".html")){
                return new Promise((resolve,reject)=>{
                    fetch(htmlUrl).then(d=>d.text()).then(d=>{
                        resolve(d)
                    })
                })
            }
            return htmlUrl;
        }

        renderHtml(html){
            if(this.reactive){
                html= eval("`"+html+"`")
                document.querySelector(this.el).innerHTML=html;
            }else{
                document.querySelector(this.el).innerHTML=html
            }
        }

        static getFilterUrl(hash) {
            return document.location.protocol + '//' + document.location.host + document.location.pathname + document.location.search + '#' + hash;
        }

        ///////////////////// //////////////////////////////////////////
        static pageRootPath="/pages/";
        static componentMap={}

        static Page(pageObj){
            let pageName = pageObj.name;
            MingRouter.Page[pageName] = pageObj;
            let htmlUrl = `${MingRouter.pageRootPath}${pageName}/${pageName}.html`;
            window.mingRouter.mapping(`/${pageName}`, htmlUrl, () => {
                pageObj.mounted && pageObj.mounted()
            });
            return pageObj;
        }

        //注册全局组件
        static registWebComponent(WrapWebComponent){
            if(!WrapWebComponent._isClass){
                let {className,tagName} = MingRouter.parseFunctionName(WrapWebComponent)
                WrapWebComponent.className=className;
                WrapWebComponent.tagName=tagName;
                //函数组件的模板
                if(MingRouter.componentMap[WrapWebComponent.className]){
                    console.error(`${WrapWebComponent.className} function is registed`);
                    return;
                }
                MingRouter.componentMap[WrapWebComponent.className] = WrapWebComponent;
                customElements.define(WrapWebComponent.tagName,
                    class App extends HTMLElement {
                        constructor() {
                            super();
                            this.attachShadow({ mode: "open" });
                        }
                        setAttribute(qualifiedName, value) {
                            super.setAttribute(qualifiedName,value);
                            this.props[qualifiedName]=value;
                            this._render(this.props);
                        }
                        async connectedCallback() {
                            let objs = {}
                            let propNames = this.getAttributeNames()
                            for (let i = 0; i < propNames.length; i++) {
                                objs[propNames[i]] = this.getAttribute(propNames[i]);
                            }
                            if (!objs.key) {
                                objs["key"] = "key";
                            }
                            objs.className = WrapWebComponent.className;
                            this.props = objs;
                            this.wrapWebComponent = this;
                            await this._render(this.props);
                        }
                        async _render(props) {
                            WrapWebComponent.template=WrapWebComponent(props);
                            let content= await WebComponent.staticGetTemplate(props)
                            this.shadowRoot.innerHTML = content;
                        }
                    });
                return;
            }

            let {className,tagName} = MingRouter.parseClassName(WrapWebComponent)
            WrapWebComponent.className=className;
            WrapWebComponent.tagName=tagName;
            if( MingRouter.componentMap[WrapWebComponent.className]){
                console.error(`${WrapWebComponent.className} is registed`);
                return;
            }
            MingRouter.componentMap[WrapWebComponent.className] = WrapWebComponent
            customElements.define(WrapWebComponent.tagName,
                class App extends HTMLElement {
                    constructor() {
                        super();
                        this.attachShadow({ mode: "open" });
                    }

                    disconnectedCallback(){

                        this.wrapWebComponent.componentWillUnmount(this.props)
                    }

                    adoptedCallback(){


                    }

                    setAttribute(qualifiedName, value) {
                        super.setAttribute(qualifiedName,value);
                        this.props[qualifiedName]=value;
                        this.wrapWebComponent.componentWillReceiveProps(this.props);
                    }


                    attributeChangedCallback(name, oldValue, newValue){

                    }
                    async connectedCallback() {
                        let objs = {}
                        let propNames = this.getAttributeNames()
                        for (let i = 0; i < propNames.length; i++) {
                            objs[propNames[i]] = this.getAttribute(propNames[i]);
                        }
                        if (!objs.key) {
                            objs["key"] = "key";
                        }
                        objs.className = WrapWebComponent.className;
                        this.props = objs;
                        this.wrapWebComponent = new WrapWebComponent(this.props);
                        WrapWebComponent[this.props["key"]] = this.wrapWebComponent;
                        this.wrapWebComponent.setState = (state) => {
                            let newState = Object.assign(this.wrapWebComponent.state, state);
                            this.wrapWebComponent.state = newState;
                            this._render(this.props)
                        };
                        await this._render(this.props);
                        this.wrapWebComponent.componentDidMount(this.props);
                    }

                    async _render() {
                        let self = this.wrapWebComponent;
                        let renderContentPure= await this.wrapWebComponent.getTemplate(this.props)
                        let renderContent = `
           
                  ${this.wrapWebComponent.renderCss? "<style>"+this.wrapWebComponent.renderCss(this.props)+"</style>":"" }
                  ${renderContentPure}`;

                        this.shadowRoot.innerHTML = renderContent

                    }
                });
        }

        static CON_FUN_NAME_RE= /function\s*(\w*)/i;
        static CON_CLASS_NAME_RE= /class\s*(\w*)/i;
        static parseFunctionName(fun){
            let className= MingRouter.CON_FUN_NAME_RE.exec(fun.toString())[1];
            let tagName= className.replace(/([A-Z])/g, "-$1").toLowerCase().substr(1);
            return {className:className,tagName:tagName};
        }
        static parseClassName(clazz){
            let className= MingRouter.CON_CLASS_NAME_RE.exec(clazz.toString())[1];
            let tagName= className.replace(/([A-Z])/g, "-$1").toLowerCase().substr(1);
            return {className,tagName};
        }
    }




    MingRouter.WebComponent=WebComponent;
    window.MingRouter=MingRouter;

    window.mingRouter = new MingRouter("#root",true);


})(window);